package com.scnu.common.config;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonValue;
import com.scnu.common.config.mvc.WrapperResponseBodyAdvice;
import com.scnu.common.config.web.GlobalExceptionHandler;
import com.scnu.common.filters.RequestIdFilter;
import com.scnu.common.interceptors.UserInfoInterceptor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
@Slf4j
@RequiredArgsConstructor
public class MvcConfig implements WebMvcConfigurer {

    private final RestTemplate restTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor(restTemplate));
    }

    @Bean
    public WrapperResponseBodyAdvice wrapperResponseBodyAdvice(){
        return new WrapperResponseBodyAdvice();
    }


    @Bean
    public RequestIdFilter requestIdFilter(){
        return new RequestIdFilter();
    }

    @Bean
    public GlobalExceptionHandler globalExceptionHandler(){
        return new GlobalExceptionHandler();
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToEnumConverterFactory());
    }

    /**
     * 通用的字符串到枚举转换器工厂
     * 检测枚举当中的 @JsonValue 注解字段，实现输入时的字符串与枚举对象的映射
     */
    @SuppressWarnings("rawtypes")
    public static class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

        @Override
        public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
            return new StringToEnumConverter<>(targetType);
        }

        @SuppressWarnings({"rawtypes","unchecked"})
        private static class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

            private static final Map<Class<?>, Map<String, ?>> ENUM_CACHE = new HashMap<>();
            private final Class<T> enumType;

            public StringToEnumConverter(Class<T> enumType) {
                this.enumType=enumType;

                T[] enums = enumType.getEnumConstants();
                Map<String, T> enumMap = (Map<String, T>) ENUM_CACHE.get(enumType);
                if (enumMap != null){
                    return;
                }
                enumMap = new HashMap<>();

                // 查找带有 @JsonValue 注解的字段
                Field jsonValueField = null;
                for (Field field : enumType.getDeclaredFields()) {
                    if (field.isAnnotationPresent(JsonValue.class)) {
                        jsonValueField = field; //关闭 Java 访问权限检查
                        field.setAccessible(true);
                        break;
                    }
                }

                // 构建映射
                for (T e : enums) {
                    enumMap.put(e.name(), e); // 枚举变量名到对象的映射

                    // 如果有 @JsonValue 字段，也添加该值的映射
                    if (jsonValueField != null) {
                        try {
                            Object value = jsonValueField.get(e);
                            if (value != null) {
                                enumMap.put(value.toString(), e);
                            }
                        } catch (IllegalAccessException ex) {
                            log.error("Error accessing JsonValue field: " + ex.getMessage());
                        }
                    }
                }
                ENUM_CACHE.put(enumType, enumMap);
            }

            @Override
            public T convert(String source) {

                Map<String, T> enumMap = (Map<String, T>) ENUM_CACHE.get(enumType);
                if (StrUtil.isEmpty( source)) {
                    return null;
                }
                return enumMap.get(source);
            }
        }
    }

}
